﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ADLite;
using System.Threading;
using AmiBrokerPlugin.Entities;
using System.Globalization;

namespace AmiBrokerPlugin
{
    public class AlfaDirectService
    {
        private IAlfaDirect ad;
        private IDictionary<string, string> tableNames = new Dictionary<string, string>();
        private IDictionary<string, string> fieldNames = new Dictionary<string, string>();

        private IList<Emitent> emitents;

        public IList<Emitent> Emitents
        {
            get
            {
                if (emitents == null)
                {
                    emitents = GetDBData<Emitent>();
                }

                return emitents;
            }
        }

        private IList<Exchange> exchanges;

        public IList<Exchange> Exchanges
        {
            get
            {
                if (exchanges == null)
                {
                    exchanges = GetDBData<Exchange>();
                }

                return exchanges;

            }
        }

        private IList<Ticker> tickers;

        public IList<Ticker> Tickers
        {
            get
            {
                if (tickers == null)
                {
                    tickers = GetDBData<Ticker>();
                }

                return tickers;
            }
        }

        private void Init()
        {
            if (ad == null)
            {
                ad = new AlfaDirect();
            }

            while (!ad.Connected)
            {
                Thread.Sleep(500);
            }
        }

        public IList<T> GetDBData<T>(string where = "") where T : new()
        {
            Init();

            object data, fields;

            ad.GetDBData(GetTableName(typeof(T)), GetFieldNames(typeof(T)), where, out data, out fields);

            return ParseData<T>(data);
        }

        private string GetTableName(Type type)
        {
            string tableName;

            if (!tableNames.TryGetValue(type.FullName, out tableName))
            {
                tableName = type.GetCustomAttributes(typeof(TableAttribute), true).OfType<TableAttribute>().FirstOrDefault().Name;
                tableNames.Add(type.FullName, tableName);
            }

            return tableName;
        }

        private string GetFieldNames(Type type)
        {
            string fields;

            if (!fieldNames.TryGetValue(type.FullName, out fields))
            {
                fields = String.Join(",", type.GetFields().Where(x => x.GetCustomAttributes(typeof(FieldAttribute), false).Any())
                    .Select(x => x.GetCustomAttributes(typeof(FieldAttribute), false).OfType<FieldAttribute>().FirstOrDefault().Name));
                fieldNames.Add(type.FullName, fields);
            }

            return fields;
        }

        private IList<T> ParseData<T>(object data) where T : new()
        {
            if (data == null)
            {
                return new List<T>();
            }

            return ((string)data).Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries).Select(x => {
                var vals = x.Split('|');
                var item = new T() as object;
                var itemProps = typeof(T).GetFields().Where(y => y.GetCustomAttributes(typeof(FieldAttribute), false).Any()).ToArray();
                for (int i = 0; i < vals.Length - 1; i++)
                {
                    if (itemProps[i].FieldType.Name == "String")
                    {
                        itemProps[i].SetValue(item, vals[i]);
                    }
                    switch (itemProps[i].FieldType.Name)
                    {
                        case "String":
                            itemProps[i].SetValue((object)item, vals[i]);
                            break;
                        case "DateTime":
                            DateTime date;

                            if (vals[i].Contains(".") || vals[i].Contains(":"))
                            {
                                date = DateTime.Parse(vals[i], CultureInfo.GetCultureInfo("ru-RU"));
                            }
                            else
                            {
                                date = DateTime.FromFileTime(long.Parse(vals[i]));
                            }

                            itemProps[i].SetValue(item, date);
                            break;
                        case "Boolean":
                            if (vals[i] == "Y")
                            {
                                itemProps[i].SetValue(item, true);
                            }
                            break;
                    }
                }
                return (T)item;
            }).ToList();
        }

        public string GetFields(string table)
        {
            Init();

            return ad.GetLocalDBStruct(table);
        }
    }
}
